import mapboxgl from 'mapbox-gl';
import type { Mapless } from '..';
import type { IdHubType } from '../core/IdHub';
import IdHub from '../core/IdHub';
import type { Handler } from '../core/Listener';
import { EmitManager } from '../core/manager';
import { getOption } from '../core/utils';
import type { Track, TrackManagerOptions } from '../index.d';

type TrackIdHubType = IdHubType & {
  trackSourceId: string;
  trackLayerId: string;
  pointSourceId: string;
  pointLayerId: string;
  pointLabelLayerId: string;
};

class TrackManager extends EmitManager<
  Record<string, unknown[]>,
  TrackIdHubType
> {
  private tracks: Track[] = [];
  private maxPoints: number;

  private _processTrack?: (track: Track) => Track;
  private _popup?: mapboxgl.Popup;

  constructor(mapless: Mapless, options?: TrackManagerOptions) {
    super(mapless, options, 'track');
    this.onInitialized();
    // 设置最大点数，默认为1000
    this.maxPoints = getOption<number>('maxPoints', options, 1000);
    this.setupHoverHandler();
  }

  override createIdHub(_idHub?: IdHubType): void {
    const newHub = new IdHub({
      layers: {
        trackLayerId: 'dzmap-track-layer',
        pointLayerId: 'dzmap-track-point-layer',
        pointLabelLayerId: 'dzmap-track-point-label-layer',
      },
      sources: {
        trackSourceId: 'dzmap-track-source',
        pointSourceId: 'dzmap-track-point-source',
      },
    }) as TrackIdHubType;
    super.createIdHub(newHub);
  }

  override initLayers() {
    if (!this.mapless) return;
    const tsid = this.idHub?.trackSourceId;
    this.mapless.addEmptySource(tsid);
    this.mapless.addLayer({
      id: this.idHub?.trackLayerId,
      type: 'line',
      source: tsid,
      paint: {
        'line-color': '#3388ff',
        'line-width': 3,
      },
    });
    const psid = this.idHub?.pointSourceId;
    this.mapless.addEmptySource(psid);
    this.mapless.addLayer({
      id: this.idHub?.pointLayerId,
      type: 'circle',
      source: psid,
      paint: {
        'circle-radius': [
          'interpolate',
          ['linear'],
          ['get', 'value'],
          0,
          5,
          100,
          15,
        ],
        'circle-color': ['get', 'color'],
        'circle-opacity': 0.8,
        'circle-stroke-width': 2,
        'circle-stroke-color': '#fff',
      },
    });
    this.mapless.addLayer({
      id: this.idHub?.pointLabelLayerId,
      type: 'symbol',
      source: psid,
      layout: {
        'text-field': ['get', 'label'],
        'text-font': ['Open Sans Regular'],
        'text-size': 12,
        'text-offset': [0, 1.5],
      },
    });
  }

  addTracks(tracks: Track[], callback?: (track: Track) => Track) {
    this._processTrack = callback || ((t) => t);
    // 处理轨迹数据
    this.tracks = tracks.map((track) => {
      const processed = callback ? callback(track) : track;
      return {
        longitude: processed.longitude,
        latitude: processed.latitude,
        label: processed.label || '',
        value: processed.value || 0,
        color: processed.color || '#3388ff',
      };
    });
    // 创建轨迹线数据
    const lineFeatures = [
      {
        type: 'Feature' as const,
        geometry: {
          type: 'LineString' as const,
          coordinates: this.tracks.map((t) => [t.longitude, t.latitude]),
        },
      },
    ];
    // 创建轨迹点数据
    const pointFeatures = this.tracks.map((track) => ({
      type: 'Feature' as const,
      geometry: {
        type: 'Point' as const,
        coordinates: [track.longitude, track.latitude],
      },
      properties: {
        label: track.label,
        value: track.value,
        color: track.color,
      },
    }));
    // 添加或更新数据源
    this.mapless?.setData(this.idHub?.trackSourceId, {
      type: 'FeatureCollection',
      features: lineFeatures as GeoJSON.Feature[],
    });
    this.mapless?.setData(this.idHub?.pointSourceId, {
      type: 'FeatureCollection',
      features: pointFeatures as GeoJSON.Feature[],
    });
  }

  addTrack(track: Track) {
    // 处理轨迹数据
    const processed = track;
    const newTrack = {
      longitude: processed.longitude,
      latitude: processed.latitude,
      label: processed.label || '',
      value: processed.value || 0,
      color: processed.color || '#3388ff',
    };
    // 添加新点并检查是否超过最大点数
    this.tracks.push(newTrack);
    if (this.tracks.length > this.maxPoints) {
      this.tracks.shift(); // 移除最旧的点
    }
    // 更新地图显示
    this.addTracks(this.tracks, this._processTrack);
  }

  private setupHoverHandler() {
    // 鼠标移入时显示popup
    const enterHandler = (e: mapboxgl.MapMouseEvent) => {
      if (!this.map || !e.features?.length) return;
      const feature = e.features[0];
      const geometry = feature.geometry as GeoJSON.Point;
      const coordinates = geometry?.coordinates.slice() as [number, number];
      const label = feature.properties?.label || '';
      const value = feature.properties?.value?.toFixed(2) || '';
      // 确保UI可见
      this.mapless?.setCursor('pointer');
      // TODO 让React|Vue端处理popup内容，或者自定义
      // const point = this.map.project(coordinates);
      // 将point（相对canvas位置）传到外部，由外部处理popup内容
      // this.mapless.emit('track:popup', { point, label, value });
      if (!this._popup) {
        // 创建并显示popup
        this._popup = new mapboxgl.Popup({
          closeButton: false,
        })
          .setLngLat(coordinates)
          .setHTML(`
          <div style="padding: 8px;">
            <div><strong>${label}</strong> : ${value}</div>
          </div>
        `)
          .addTo(this.map);
        this._popup.on('close', () => {
          this._popup = undefined;
        });
      } else {
        // 更新popup内容
        this._popup.setLngLat(coordinates).setHTML(`
          <div style="padding: 8px;">
            <div><strong>${label}</strong> : ${value}</div>
          </div>`);
      }
    };
    this._listener?.add(
      'mouseenter',
      this.idHub?.pointLayerId,
      enterHandler as Handler,
    );
    const leaveHandler = () => {
      this.mapless?.setCursor('');
    };
    this._listener?.add(
      'mouseleave',
      this.idHub?.pointLayerId,
      leaveHandler as Handler,
    );
  }

  override setVisible(visible: boolean): void {
    super.setVisible(visible);
    if (!visible) this._popup?.remove();
  }

  override destroy = () => {
    super.destroy();
    this._popup?.remove();
    this._popup = undefined;
    this.tracks = [];
  };
}

export default TrackManager;

export const attachTrack = (
  mapless: Mapless | undefined,
  name?: string,
  options?: TrackManagerOptions,
) => mapless?.use(TrackManager, name, options);
export const getTrack = (mapless: Mapless | undefined, name?: string) =>
  mapless?.get(TrackManager, name);
export const detachTrack = (mapless: Mapless | undefined, name?: string) =>
  mapless?.unuse(TrackManager, name);
